package Server;

import com.alibaba.fastjson.*;
import com.me.websocket.WsPool;
import org.java_websocket.WebSocket;

import java.util.List;
import java.util.Map;

// 负责解析报文并构造响应报文
public class Message {
    public static JSONObject response(JSONObject receive, WebSocket webSocket) {
        if (null == receive.get("handler")) {
            return error(receive);
        }
        else if (receive.getString("handler").equals("/client/init")) {
            return init(receive, webSocket);
        }
        else if (receive.getString("handler").equals("/manager/login")) {
            return login(receive);
        }
        else if (receive.getString("handler").equals("/manager/logout")) {
            return logout(receive);
        }
        else if (receive.getString("handler").equals("/manager/openRoom")) {
            return openRoom(receive);
        }
        else if (receive.getString("handler").equals("/manager/seeRoomInfo")) {
            return seeRoomInfo(receive);
        }
        else if (receive.getString("handler").equals("/manager/getRoomList")) {
            return getRoomList(receive);
        }
        else if (receive.getString("handler").equals("/manager/controlRoom")) {
            return managerControlRoom(receive);
        }
        else if (receive.getString("handler").equals("/manager/simpleCost")) {
            return simpleCost(receive);
        }
        else if (receive.getString("handler").equals("/manager/detailCost")) {
            return adminDetailCost(receive);
        }
        else if (receive.getString("handler").equals("/manager/sendMsg")) {
            return sendMsg(receive);
        }
        else if (receive.getString("handler").equals("/manager/detailCost")) {
            return managerDetailCost(receive);
        }
        else if (receive.getString("handler").equals("/manager/detailCostFile")) {
            return detailCostFile(receive);
        }
        else if (receive.getString("handler").equals("/manager/requestReport")) {
            return requestReport(receive);
        }
        else if (receive.getString("handler").equals("/manager/closeRoom")) {
            return closeRoom(receive);
        }
        else if (receive.getString("handler").equals("/client/open")) {
            return open(receive);
        }
        else if (receive.getString("handler").equals("/client/set")) {
            return set(receive);
        }
        else if (receive.getString("handler").equals("/client/controlRoom")) {
            return clientControlRoom(receive);
        }
        else if (receive.getString("handler").equals("/client/updateStatus")) {
            return updateStatus(receive);
        }
        else if (receive.getString("handler").equals("/client/error")) {
            return clientError(receive);
        }
        else if (receive.getString("handler").equals("/client/contact")) {
            return contact(receive);
        }
        else if (receive.getString("handler").equals("/client/confirm")) {
            System.out.println("Receive client confirm");
            return null;
        }
        else {
            return error(receive);
        }
    }

    public static DB db = new DB();

    // 构造 error 响应报文
    private static JSONObject error(JSONObject receive) {
        String refId = receive.getString("refId");
        if (null == refId) {
            return null;
        }
        Construct construct = new Construct(refId, "/server/error");
        construct.setMsg("Bad Request");
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 构造初始化响应报文
    private static JSONObject init(JSONObject receive, WebSocket webSocket) {
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        Data data = new Data();

        String roomId = dataJson.getString("roomId");
        String IP = WsPool.getIpByWs(webSocket);
        double initTmp = dataJson.getDoubleValue("initTmp");

        if (!db.roomInit(roomId, IP, initTmp)) {// roomId已初始化
            data.setAck(false);
            construct.setHandler("/server/error");
            construct.setMsg("Room has already been initialized");
        }
        else {
            WsPool.addUser(roomId, webSocket);
            data.setAck(true);
        }
        construct.setData(data);
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 管理员登录
    private static JSONObject login(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/retRole");

        String adminId = dataJson.getString("adminId");
        String password = dataJson.getString("password");

        String roleToken = db.retRole(adminId, password);
        if (roleToken.length() > 1) {
            Data data = new Data();
            String[] valueString = roleToken.split(",");
            String role;
            role = valueString[0].equals("" + User.ADMIN) ? "admin" : "manager";
            data.setRole(role);
            data.setToken(valueString[1]);
            construct.setData(data);
        }
        else {
            construct.setMsg("Wrong adminId or password");
            construct.setHandler("/server/error");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // TODO : 管理员注销 ******************
    private static JSONObject logout(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        String adminId = dataJson.getString("adminId");

//        if (!db.logout(adminId)) {
//            construct.setHandler("/server/error");
//            construct.setMsg("Fail to logout, please check again");
//        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 管理员开房
    private static JSONObject openRoom(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        String roomId = dataJson.getString("roomId");
        double defaultTmp = dataJson.getDoubleValue("defaultTmp");
        String token = receive.getString("token");

        String roomtoken = db.openRoom(roomId, defaultTmp);
        WebSocket dest = WsPool.getWsByUser(roomId);
        int role = db.verifyUser(token);
//        System.out.println("************************************");
//        System.out.println("" + role + " : " + roomtoken + " : " + dest);
//        System.out.println("************************************");

        if ((role == User.ADMIN || role == User.MANAGER) && roomtoken.length() > 1 && dest != null) {
            // 开房
            // 构造开房报文，并调用WsPool的方法向房间发送开房报文
            Construct Json = new Construct();
            Json.setRefId(receive.getString("refId"));
            Json.setHandler("/server/openRoom");
            Data data = new Data();
            data.setRoomId(roomId);
            data.setDefaultTmp(defaultTmp);
            data.setToken(roomtoken);
            Json.setData(data);
            JSONObject message = (JSONObject) JSONObject.toJSON(Json);
            // 通过roomId获取websocket连接
            WsPool.sendMessageToUser(dest, message.toString());
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room already exists");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 查询房间内部信息
    private static JSONObject seeRoomInfo(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/roomInfo");
        Data data = new Data();
        String token = receive.getString("token");
        String roomId = dataJson.getString("roomId");
        List<Map<String, Object>> roomInfoList;
        int role = db.verifyUser(token);

        if (role == User.ADMIN || role == User.MANAGER) {
            if (roomId.equals("-1")) { //查看全部房间信息
                roomInfoList = db.seeAllRoomInfo();
            }
            else { //查看指定房间
                roomInfoList = db.seeRoomInfo(roomId);
            }
            data.setRoomInfoList(roomInfoList);

        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required");
        }

        construct.setData(data);
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 查看房间外部状态
    private static JSONObject getRoomList(JSONObject receive) {
        Construct construct = new Construct(receive.getString("refId"), "/server/retRoomList");
        Data data = new Data();

        String token = receive.getString("token");

        List<Map<String, Object>> roomList;
        int role = db.verifyUser(token);

        if (role == User.ADMIN || role == User.MANAGER) {
            roomList = db.getRoomList();
            data.setRoomList(roomList);
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required");
        }
        construct.setData(data);
        return (JSONObject) JSONObject.toJSON(construct);
    }

    private static JSONObject clientControlRoom(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        // TODO : 后期加上token与roomId的对应判断
        int role = db.verifyUser(token);

        if (role == User.GUEST) {
            String roomId = dataJson.getString("roomId");
            int power = dataJson.getInteger("power");
            int tmp = dataJson.getInteger("tmp");
            int wind = dataJson.getInteger("wind");

            boolean success = db.controlRoom(roomId, power, tmp, wind);
            if (!success) {
                construct.setHandler("/server/error");
                construct.setMsg("Operation Failed");
            }
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 管理员控制房间空调
    private static JSONObject managerControlRoom(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        String roomId = dataJson.getString("roomId");
        int power = dataJson.getInteger("power");
        double tmp = dataJson.getDoubleValue("tmp");
        int wind = dataJson.getInteger("wind");
        WebSocket dest = WsPool.getWsByUser(roomId);
        int role = db.verifyUser(token);

        if ((role == User.ADMIN || role == User.MANAGER) && dest != null) {
            if (db.controlRoom(roomId, power, tmp, wind)) {
                Construct Json = new Construct();
                Json.setRefId(receive.getString("refId"));
                Json.setHandler("/server/setRoom");
                Data data = new Data();
                data.setRoomId(roomId);
                data.setPower(power);
                data.setTmp(tmp);
                data.setWind(wind);
                Json.setData(data);
                JSONObject message = (JSONObject) JSONObject.toJSON(Json);
                // 获取对应房间的websocket连接
                WsPool.sendMessageToUser(dest, message.toString());
            }
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room not exist");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 账单
    private static JSONObject simpleCost(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/retSimpleCost");

        String roomId = dataJson.getString("roomId");

        String simpleCost = db.simpleCost(roomId);

        if (simpleCost.length() > 1) {
            Data data = new Data();
            String[] value = simpleCost.split(",");
            data.setRoomId(roomId);
            data.setBeginTime(value[2]);
            data.setEndTime(value[3]);
            data.setCost(Double.parseDouble(value[1]));
            construct.setData(data);
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room not exist");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // TODO : adminDetailCost
    private static JSONObject adminDetailCost(JSONObject receive) {
        return retTest(receive);
    }

    // TODO : sendMsg
    private static JSONObject sendMsg(JSONObject receive) {
        return retTest(receive);
    }


    private static JSONObject managerDetailCost(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/retDetailCost");

        String roomId = dataJson.getString("roomId");
        List<Map<String, Object>> detailCostList;
        int role = db.verifyUser(token);

        if (role == User.MANAGER || role == User.ADMIN) {
            Data data = new Data();
            detailCostList = db.detailCost(roomId);
            data.setDetailCostList(detailCostList);
            construct.setData(data);
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // TODO : detailCostFile
    private static JSONObject detailCostFile(JSONObject receive) {
        return retTest(receive);
    }

    // TODO : requestReport
    private static JSONObject requestReport(JSONObject receive) {
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/retReport");

        List<Map<String, Object>> roomReportList;
        int role = db.verifyUser(token);

        if (role == User.ADMIN || role == User.MANAGER) {
            Data data = new Data();
            roomReportList = db.requestReport();
            data.setRoomReportList(roomReportList);
            construct.setData(data);
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    // 管理员退房
    private static JSONObject closeRoom(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        String roomId = dataJson.getString("roomId");
        int role = db.verifyUser(token);
        WebSocket dest = WsPool.getWsByUser(roomId);
        if (role == User.ADMIN || role == User.MANAGER) {
            boolean checkOutSuccess = db.checkOut(roomId);
            boolean deleteGuestInfo = db.deleteGuestInfo(roomId);
//            boolean deleteConn = WsPool.removeUser(dest);
//            System.out.println(checkOutSuccess + "," + deleteGuestInfo + "," + deleteConn);
            if (!checkOutSuccess || !deleteGuestInfo /*|| !deleteConn*/) {
                construct.setHandler("/server/error");
                construct.setMsg("Operation Failed");
            }

            // 向房间发送退房报文
            Construct Json = new Construct();
            Json.setRefId(receive.getString("refId"));
            Json.setHandler("/server/closeRoom");
            JSONObject message = (JSONObject) JSONObject.toJSON(Json);
            // 通过roomId获取websocket连接
            WsPool.sendMessageToUser(dest, message.toString());
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room not open");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    private static JSONObject open(JSONObject receive) {//开空调
        JSONObject dataJson = receive.getJSONObject("data");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        String token = receive.getString("token");
        int power = dataJson.getInteger("power");

        if (db.open(token, power)) {
            construct.setMsg("Success");
        }
        else {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room not exist");
        }
        return (JSONObject) JSONObject.toJSON(construct);
    }

    private static JSONObject set(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");

        int tmp = dataJson.getInteger("tmp");
        int wind = dataJson.getInteger("wind");

        if (!db.set(token, tmp, wind)) {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room not exist");
        }

        return (JSONObject) JSONObject.toJSON(construct);
    }

    private static JSONObject updateStatus(JSONObject receive) {
        JSONObject dataJson = receive.getJSONObject("data");
        String token = receive.getString("token");
        Construct construct = new Construct(receive.getString("refId"), "/server/updateMoney");

        double nowTmp = dataJson.getDoubleValue("nowTmp");
        int role = db.verifyUser(token);
        double fee = db.updateStatus(token, nowTmp);

        // 错误
        if (role != User.GUEST || fee < 0) {
            construct.setHandler("/server/error");
            construct.setMsg("Permission required or room not open");
        }
        else {
            Data data = new Data();
            data.setTotalFee(fee);
            construct.setData(data);
        }

        return (JSONObject) JSONObject.toJSON(construct);
    }

    // TODO : clientError
    private static JSONObject clientError(JSONObject receive) {
        return retTest(receive);
    }

    // TODO : contact
    private static JSONObject contact(JSONObject receive) {
        return retTest(receive);
    }

    // TODO : 专为测试构造的包, 代码补全后需要删除该方法
    private static JSONObject retTest(JSONObject receive) {
        Construct construct = new Construct(receive.getString("refId"), "/server/confirm");
        construct.setMsg("This is a test response");
        return (JSONObject) JSONObject.toJSON(construct);
    }
}


// 辅助构造响应报文
class Construct {
    private String refId;
    private String handler;
    private String msg;
    private JSONObject data;

    public Construct() {
    }

    public Construct(String refId) {
        this.refId = refId;
    }

    public Construct(String refId, String handler) {
        this.refId = refId;
        this.handler = handler;
    }

    public String getRefId() {
        return refId;
    }

    public void setRefId(String refId) {
        this.refId = refId;
    }

    public String getHandler() {
        return handler;
    }

    public void setHandler(String handler) {
        this.handler = handler;
    }

    public JSONObject getData() {
        return data;
    }

    public void setData(Data data) {
        this.data = (JSONObject) JSONObject.toJSON(data);
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}


// 构造 data 字段
class Data {
    private String roomId;
    private String msg;
    private Boolean ack;
    private Boolean result;
    private String role;
    private String token;
    private Double initTmp;
    private Double defaultTmp;
    private Double nowTmp;
    private Boolean idle;
    private Integer power;
    private Double tmp;
    private Integer wind;
    private String beginTime;
    private String endTime;
    private Double cost;
    private Double fee;
    private Double totalFee;
    private List<Map<String, Object>> roomInfoList;
    private List<Map<String, Object>> roomList;
    private List<Map<String, Object>> detailCostList;
    private List<Map<String, Object>> roomReportList;

    public Double getTotalFee() {
        return totalFee;
    }

    public void setTotalFee(Double totalFee) {
        this.totalFee = totalFee;
    }

    public Double getFee() {
        return fee;
    }

    public void setFee(Double fee) {
        this.fee = fee;
    }

    public String getRoomId() {
        return roomId;
    }

    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Boolean getAck() {
        return ack;
    }

    public void setAck(Boolean ack) {
        this.ack = ack;
    }

    public Boolean getResult() {
        return result;
    }

    public void setResult(Boolean result) {
        this.result = result;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public Double getInitTmp() {
        return initTmp;
    }

    public void setInitTmp(Double initTmp) {
        this.initTmp = initTmp;
    }

    public Double getDefaultTmp() {
        return defaultTmp;
    }

    public void setDefaultTmp(Double defaultTmp) {
        this.defaultTmp = defaultTmp;
    }

    public Double getNowTmp() {
        return nowTmp;
    }

    public void setNowTmp(Double nowTmp) {
        this.nowTmp = nowTmp;
    }

    public Boolean getIdle() {
        return idle;
    }

    public void setIdle(Boolean idle) {
        this.idle = idle;
    }

    public Integer getPower() {
        return power;
    }

    public void setPower(Integer power) {
        this.power = power;
    }

    public Double getTmp() {
        return tmp;
    }

    public void setTmp(Double tmp) {
        this.tmp = tmp;
    }

    public Integer getWind() {
        return wind;
    }

    public void setWind(Integer wind) {
        this.wind = wind;
    }

    public String getBeginTime() {
        return beginTime;
    }

    public void setBeginTime(String beginTime) {
        this.beginTime = beginTime;
    }

    public String getEndTime() {
        return endTime;
    }

    public void setEndTime(String endTime) {
        this.endTime = endTime;
    }

    public Double getCost() {
        return cost;
    }

    public void setCost(Double cost) {
        this.cost = cost;
    }

    public List<Map<String, Object>> getRoomInfoList() {
        return roomInfoList;
    }

    public void setRoomInfoList(List<Map<String, Object>> roomInfoList) {
        this.roomInfoList = roomInfoList;
    }

    public List<Map<String, Object>> getRoomList() {
        return roomList;
    }

    public void setRoomList(List<Map<String, Object>> roomList) {
        this.roomList = roomList;
    }

    public List<Map<String, Object>> getDetailCostList() {
        return detailCostList;
    }

    public void setDetailCostList(List<Map<String, Object>> detailCostList) {
        this.detailCostList = detailCostList;
    }

    public List<Map<String, Object>> getRoomReportList() {
        return roomReportList;
    }

    public void setRoomReportList(List<Map<String, Object>> roomReportList) {
        this.roomReportList = roomReportList;
    }
}
